package stat

import (
	"fmt"
	"sync"
	"time"

	"gitee.com/les49/esgo/uuid"

	"github.com/go-co-op/gocron"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/kv"
)

const (
	DEVICE_ONLINE_STAT_PREFIX        = "do"  /* 设备在线 */
	DEVICE_DALIY_ACTIVE_STAT_PREFIX  = "dda" /* 日激活量 */
	DEVICE_DALIY_ONLINE_STAT_PREFIX  = "ddo" /* 日活量，日在线统计 */
	DEVICE_WEEKLY_ONLINE_STAT_PREFIX = "dwo" /* 周在线统计 */
	DEVICE_PROVINCE_MAP_STAT_PREFIX  = "dpm" /* 设备地图省份统计, dpm:${uid}:${pid}:${code}, code是省份编码 */

	DEVICE_LOGIN_TIMEOUT_SEC = 600
)

type (
	DeviceStat struct {
		store   kv.Store
		lock    sync.Mutex
		uidList map[string]uint32
	}
)

func getDateString() string {
	t := time.Now()
	return fmt.Sprintf("%04d%02d%02d", t.Local().Year(), t.Local().Month(), t.Local().Day())
}

func getWeeklyString() string {
	t := time.Now()
	return fmt.Sprintf("%04d%02d", t.Local().Year(), t.YearDay()/7)
}

func userIdToString(uid uint32) string {
	uidStr := uuid.UidToString(uid)
	return uidStr
}

func getOnlineKey(uid uint32) string {
	uidStr := userIdToString(uid)
	return uidStr + ":" + DEVICE_ONLINE_STAT_PREFIX
}

func getDaliyOnlineKey(uid uint32) string {
	dateString := getDateString()
	uidStr := userIdToString(uid)
	return uidStr + ":" + DEVICE_DALIY_ACTIVE_STAT_PREFIX + ":" + dateString
}

func getWeeklyOnlineKey(uid uint32) string {
	dateString := getDateString()
	uidStr := userIdToString(uid)
	return uidStr + ":" + DEVICE_WEEKLY_ONLINE_STAT_PREFIX + ":" + dateString
}

func (s *DeviceStat) addDaliyOnline(uid uint32, sn string) {
	key := getDaliyOnlineKey(uid)
	s.store.Sadd(key, sn)
}

func (s *DeviceStat) addWeeklyOnline(uid uint32, sn string) {
	key := getWeeklyOnlineKey(uid)
	s.store.Sadd(key, sn)
}

func (s *DeviceStat) doDaliySyncTask() {

}

func (s *DeviceStat) doClearOnlineDeviceTask() {
	score := time.Now().Unix() - DEVICE_LOGIN_TIMEOUT_SEC
	for _, uid := range s.uidList {
		key := getOnlineKey(uid)
		s.store.Zremrangebyscore(key, 0, score)
	}
}

func NewDeviceStat(c cache.ClusterConf) (*DeviceStat, error) {
	devStat := &DeviceStat{
		store:   kv.NewStore(c),
		uidList: make(map[string]uint32, 10),
	}

	s := gocron.NewScheduler(time.UTC)
	s.Every(1).Day().At("00:30").Do(func() {
		devStat.doDaliySyncTask()
	})
	s.Every(1).Minute().Do(func() {
		devStat.doClearOnlineDeviceTask()
	})
	s.StartAsync()

	return devStat, nil
}

func (s *DeviceStat) DeviceLogin(uid uint32, sn string) {
	key := getOnlineKey(uid)
	score := time.Now().Unix()
	s.lock.Lock()
	s.store.Zadd(key, score, sn)
	s.addDaliyOnline(uid, sn)
	s.addWeeklyOnline(uid, sn)
	s.uidList[userIdToString(uid)] = uid
	s.lock.Unlock()
}

func (s *DeviceStat) DeviceIsLogin(uid uint32, sn string) bool {
	key := getOnlineKey(uid)
	v, err := s.store.Zscore(key, sn)
	if err != nil {
		return false
	}

	if (time.Now().Unix() - v) > DEVICE_LOGIN_TIMEOUT_SEC {
		return false
	}
	return true
}

func (s *DeviceStat) DeviceHeartbeat(uid uint32, sn string) (isLogin bool) {
	if !s.DeviceIsLogin(uid, sn) {
		return false
	}

	s.DeviceLogin(uid, sn)
	return true
}

func (s *DeviceStat) DeviceIsOnline(uid uint32, sn string) bool {
	return s.DeviceIsLogin(uid, sn)
}

func (s *DeviceStat) DeviceOffline(uid uint32, sn string) error {
	return nil
}

func (s *DeviceStat) DeviceOnlineCount(uid uint32) uint32 {
	key := getOnlineKey(uid)
	c, err := s.store.Zcard(key)
	if err != nil {
		return 0
	}

	return uint32(c)
}

func (s *DeviceStat) DeviceDaliyOnlineCount(uid uint32) uint32 {
	key := getDaliyOnlineKey(uid)
	c, err := s.store.Scard(key)
	if err != nil {
		return 0
	}

	return uint32(c)
}

func (s *DeviceStat) DeviceWeeklyOnlineCount(uid uint32) uint32 {
	key := getWeeklyOnlineKey(uid)
	c, err := s.store.Scard(key)
	if err != nil {
		return 0
	}

	return uint32(c)
}
